﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace PlanetViewer
{
    public class Heightmap
    {
        private float[] _Data;
        public int[] Anchors;
        public int Seed;
        public float _SeaLevel;

        public List<DetailGroup> _dGroups;
        public List<int> _dGroupPos;

        public Terrain Terrain;

        private int _Length { get { return this._Data.Length; } }

        private float _minHeight = -1.0f;
        private float _maxHeight = 1.5f;

        private int _minFeatureLength = 60;
        private int _maxFeatureLength = 150;

        public Heightmap(Terrain terrain, int length, int seed = 0, float roughness = 1.0f, float sealevel = 0f, int minspacing = 0, int maxspacing = 0)
        {
            this.Terrain = terrain;

            if (minspacing == 0) minspacing = _minFeatureLength;
            if (maxspacing == 0) maxspacing = _maxFeatureLength;

            if (seed == 0) seed = new Random().Next();
            this.Seed = seed;
            this._SeaLevel = sealevel;

            Random rand = new Random(seed);

            float[] Heightmap = new float[length];
            this._dGroups = new List<DetailGroup>();
            this._dGroupPos = new List<int>();
            
            int neg = 1;

            List<int> anchors = new List<int>();

            int newSpacing = minspacing + rand.Next(maxspacing - minspacing);

            for (int s = 0; s < length - minspacing; s += newSpacing)
            {
                Heightmap[s] = sealevel; //Create anchor points for water boundaries
                neg *= -1;

                anchors.Add(s); //save the anchor point for later (smoothing and water)

                if (s > 0)
                {
                    int dGPos = s - newSpacing; //Position of the working anchor group

                    DetailGroup dGroup = new DetailGroup(newSpacing, rand, neg);
                    this._dGroups.Add(dGroup);
                    this._dGroupPos.Add(dGPos);
                    dGroup.Data.CopyTo(Heightmap, dGPos);
                }

                newSpacing = minspacing + rand.Next(maxspacing - minspacing);

                if (s + newSpacing >= length - minspacing) //Create the final anchor group back to s = 0
                {
                    neg *= -1;
                    int gLength = length - s;

                    DetailGroup dGroup = new DetailGroup(gLength, rand, neg);
                    this._dGroups.Add(dGroup);
                    this._dGroupPos.Add(s);
                    dGroup.Data.CopyTo(Heightmap, s);                 
                }
            }

            Anchors = new int[anchors.Count];
            anchors.CopyTo(Anchors);

            this._Data = Heightmap;
        }

        public float GetData(int index, int LOD = 0)
        {
            index *= (LOD + 1);

            int wrap = util.Modulo(index, this._Length);

            return this._Data[wrap];
        }

        public void SetSpineHeight(int spine, float height)
        {
            spine = util.Modulo(spine, this._Length);

            if (height < _minHeight) height = _minHeight;
            if (height > _maxHeight) height = _maxHeight;

            this._Data[spine] = height;

            this.Terrain.RefreshVertexHeight(spine);
        }
    }
}
